package com.example.game;

import java.awt.*;
import java.util.Arrays;

/**
 * @author: HanTing Wang
 * @description:
 * @version:
 * @date: 2022/1/1
 */
public class ChessBoard {
    public static final int SIDE_NUM = 15;
    public static final int BLANK = 0;
    public static final int BLACK = 1;
    public static final int WHITE = 2;
    public static final int LIMIT = 4;

    private int[] tempPosition = {-1, -1};

    private int[][] boardArr;

    private int role;

    public ChessBoard() {
        init();
    }

    private void init() {
        this.role = BLACK;
        this.boardArr = new int[SIDE_NUM][SIDE_NUM];
    }

    public boolean checkPositionBoard(int x, int y) {
        return x >= 0 && y >= 0 && x < SIDE_NUM && y < SIDE_NUM;
    }

    public boolean checkPositionAvailable(int x, int y) {
        return checkPositionBoard(x, y) && this.boardArr[y][x] == BLANK;
    }

    public boolean dropChess(int role, int x, int y) {
        if (role != this.role || !checkPositionAvailable(x, y)) {
            return false;
        }
        this.boardArr[y][x] = role;
        this.role = this.role == BLACK ? WHITE : BLACK;
        return true;
    }

    public int checkWin() {
        int[] dx = {1, 0, 1, -1};
        int[] dy = {0, 1, 1, 1};
        int[] px = {0, 1};
        int[] py = {1, 0};
        for (int r = 0; r < 2; ++r) {
            int ox = 0, oy = 0;
            for (int i = 0; i < SIDE_NUM; ++i) {
                int x = ox, y = oy;
                int count = 0, last = BLANK;
                while (checkPositionBoard(x, y)) {
                    if (boardArr[y][x] == BLANK) {
                        count = 0;
                    } else if (boardArr[y][x] == last) {
                        ++count;
                    } else {
                        count = 1;
                    }

                    if (count > 4) {
                        return last;
                    }

                    last = boardArr[y][x];
                    x += dx[r];
                    y += dy[r];
                }
                ox += px[r];
                oy += py[r];
            }
        }
        int[] ox = {0, 0, 0, SIDE_NUM - 1};
        int[] oy = {SIDE_NUM - 1, 0, 0, 0};
        int[] kx = {0, 1, 1, 0};
        int[] ky = {-1, 0, 0, 1};
        for (int r = 0; r < 4; ++r) {
            int gx = ox[r], gy = oy[r];
            for (int i = 0; i < SIDE_NUM; ++i) {
                int x = gx, y = gy;
                int count = 0, last = BLANK;
                while (checkPositionBoard(x, y)) {
                    if (boardArr[y][x] == BLANK) {
                        count = 0;
                    } else if (boardArr[y][x] == last) {
                        ++count;
                    } else {
                        count = 1;
                    }

                    if (count > 4) {
                        return last;
                    }
                    last = boardArr[y][x];
                    x += dx[2 + (r >> 1)];
                    y += dy[2 + (r >> 1)];
                }
                gx += kx[r];
                gy += ky[r];
            }
        }
        return BLANK;
    }

    public void clearBoard() {
        this.init();
    }

    public void printChessBoard() {
        for (int[] line : this.boardArr) {
            System.out.println(Arrays.toString(line));
        }
        System.out.println("present role: " + role);
        System.out.println("-------------------------------------------------------");
    }

    public boolean AIDrop() {
        getBestDrop(this.role);
        return false;
//        int[] result = getBestDrop(this.role);
//        if (result == null) {
//            return false;
//        } else {
//            dropChess(this.role, result[0], result[1]);
//            return true;
//        }
    }

    private static final int[] dx = {1, 0, 1,-1};
    private static final int[] dy = {0, 1, 1, 1};

    private static final int[][] scores =
            {{0, 0, 0},
                    {0, 5, 5},
                    {5, 8, 100},
                    {10, 500, 3000},
                    {20, 2500, 300000},
                    {1000000, 1000000, 1000000}};

    /**
     * return ai best drop of the color
     *
     * @param color:
     * @return int[], length = 2, [x, y]
     */
    public void getBestDrop(int color) {
        int result = abTree(color, color, 1, Integer.MAX_VALUE);
        System.out.println(Arrays.toString(tempPosition) + " " + result);
    }

    private boolean checkBoard(int x, int y) {
        return x >= 0 && x < SIDE_NUM && y >= 0 && y < SIDE_NUM;
    }

    private int[] getLine(int x, int y, int color, int pos) {
        int[] results = new int[5];
        if (!checkBoard(x, y) || boardArr[y][x] == BLANK) {
            results[0] = 0;
            return results;
        }
        int sum = 1, hx = x + dx[pos], hy = y + dy[pos], lx = x - dx[pos], ly = y - dy[pos];
        for (; checkBoard(hx, hy) && boardArr[hy][hx] == color; hx += dx[pos], hy += dy[pos]) ++sum;
        for (; checkBoard(lx, ly) && boardArr[ly][lx] == color; lx -= dx[pos], ly -= dy[pos]) ++sum;
        results[0] = sum;
        results[1] = lx;
        results[2] = ly;
        results[3] = hx;
        results[4] = hy;
        return results;
    }

    private int evaluate(int color, int x, int y) {
        int result = 0;
        if (checkBoard(x, y) && (color == BLACK || color == WHITE)) {
            result += 14 - (Math.abs(x - (SIDE_NUM >> 1)) + Math.abs(y - (SIDE_NUM >> 1)));
            for (int r = 0; r < 4; ++r) {
                int[] line;
                int temp = boardArr[y][x];
                boardArr[y][x] = color;
                line = getLine(x, y, color, r);
                boardArr[y][x] = temp;

                int length = line[0];
                int alive = 0;
                if (checkBoard(line[1], line[2]) && boardArr[line[2]][line[1]] == BLANK) ++ alive;
                if (checkBoard(line[3], line[4]) && boardArr[line[4]][line[3]] == BLANK) ++ alive;

                result += scores[length][alive];
                switch(length) {
                    case 5:
                        return result;
                    case 4:
                        break;
                    case 3:
                        if (alive > 0) {
                            int[] tLine;
                            if (checkBoard(line[1], line[2]) && boardArr[line[2]][line[1]] == BLANK) {
                                boardArr[line[2]][line[1]] = color;
                                tLine = getLine(line[1]-dx[r], line[2]-dy[r], color, r);
                                result += Math.max(0, (tLine[0] - 3)) * 1500;
                                boardArr[line[2]][line[1]] = BLANK;
                            }
                            if (checkBoard(line[3], line[4]) && boardArr[line[4]][line[3]] == BLANK) {
                                boardArr[line[4]][line[3]] = color;
                                tLine = getLine(line[3]-dx[r], line[4]-dy[r], color, r);
                                result += Math.max(0, (tLine[0] - 3)) * 1500;
                                boardArr[line[4]][line[3]] = BLANK;
                            }
                        }
                        break;
                    case 2:
                        if (alive > 0) {
                            int[] tLine;
                            if (checkBoard(line[1], line[2]) && boardArr[line[2]][line[1]] == BLANK) {
                                boardArr[line[2]][line[1]] = color;
                                tLine = getLine(line[1]-dx[r], line[2]-dy[r], color, r);
                                result += Math.max(0, (tLine[0] - 3)) * 1000;
                                boardArr[line[2]][line[1]] = BLANK;
                            }
                            if (checkBoard(line[3], line[4]) && boardArr[line[4]][line[3]] == BLANK) {
                                boardArr[line[4]][line[3]] = color;
                                tLine = getLine(line[3]-dx[r], line[4]-dy[r], color, r);
                                result += Math.max(0, (tLine[0] - 3)) * 1000;
                                boardArr[line[4]][line[3]] = BLANK;
                            }
                        }
                        break;
                    case 1:
                        break;
                }
            }
        }
        return result;
    }

    private int abTree(int target, int color, int deep, int upScore) {
        int arg = target == color ? 1 : -1;
        int present = -Integer.MAX_VALUE;
        for (int i = 0; i < SIDE_NUM; i++) {
            for (int j = 0; j < SIDE_NUM; j++) {
                if (boardArr[i][j] != BLANK) continue;
                boardArr[i][j] = color;
                int score = evaluate(color, j, i);
                if (score > 800000) {
                    boardArr[i][j] = BLANK;
                    tempPosition[0] = j;
                    tempPosition[1] = i;
                    return 1000000;
                }
                score = deep == LIMIT ? score : abTree(target, WHITE+BLACK-color, deep+1, Math.abs(present));
                boardArr[i][j] = BLANK;

                if (arg * score > arg * upScore) {
                    return score;
                }

                if (arg * score > present) {
                    present = arg * score;
                    tempPosition[0] = j;
                    tempPosition[1] = i;
                }

            }
        }
        return arg * present;
    }

    private void printScores(int color) {
        for (int i = 0; i < SIDE_NUM; ++i) {
            for (int j = 0; j < SIDE_NUM; ++j) {
                if (boardArr[i][j] != BLANK) {
                    System.out.print("\t\t");
                } else {
                    boardArr[i][j] = color;
                    int score = evaluate(BLACK, j, i);
                    System.out.print(String.format("%7d ", score));
                    boardArr[i][j] = BLANK;
                }
            }
            System.out.println();
        }
        System.out.println("---------------------------------------------------");
    }

}
